#pragma once
#ifndef __SIMPLE_PATH_TRACER_HPP__
#define __SIMPLE_PATH_TRACER_HPP__

#include "scene/Scene.hpp"
#include "Ray.hpp"
#include "Camera.hpp"
#include "intersections/HitRecord.hpp"
#include "ViewPoint.hpp"
#include "shaders/ShaderCreator.hpp"
#include "kdTree.hpp"
#include <tuple>
#include <mutex>
namespace PhotonMapping
{
    using namespace NRenderer;
    using namespace std;

    class PhotonMappingRenderer
    {
    public:
        SharedScene spScene;
        Scene& scene;

        unsigned int width;
        unsigned int height;
        unsigned int depth;
        unsigned int samples;
        bool photonStage;
        float lightStrength;
        float photonR;

        using PCam = PhotonMapping::Camera;
        PCam camera;

        vector<SharedShader> shaderPrograms;

        vector<ViewPoint> ViewPoints;
        mutex pointMutex;
        kdTree* tree;
        Vec3* renderpic;
        int* samplenum;
        int photonNum = 100000;
        float queryRadius = 3.f;
        int rounds = 10;
        float energy = 0.8f / log(rounds);

    public:
        PhotonMappingRenderer(SharedScene spScene)
            : spScene(spScene)
            , scene(*spScene)
            , camera(spScene->camera)
        {
            width = scene.renderOption.width;
            height = scene.renderOption.height;
            depth = scene.renderOption.depth;
            samples = scene.renderOption.samplesPerPixel;

            photonStage = false;
            lightStrength = 1.0 / log(samples);
            photonR = 4;
            Vec3 v{ 0,0,0 };
            renderpic = new Vec3[height * width]{};
            samplenum = new int[height * width];
            for (int i = 0; i < height*width; i++) {
                renderpic[i] = v;
                samplenum[i] = 0;
            }
        }
        ~PhotonMappingRenderer() = default;

        using RenderResult = tuple<RGBA*, unsigned int, unsigned int>;
        RenderResult render();
        void release(const RenderResult& r);

    private:
        void rayTask(RGBA* pixels, int width, int height, int off, int step);
        void photonTask(RGBA* pixels, int width, int height, int off, int step);

        RGB gamma(const RGB& rgb);
        void rayTrace(const Ray& r, int currDepth, float lambda, int X, int Y);
        void photonTrace(const Ray& ray, Vec3 rayColor, int currDepth);
        HitRecord closestHitObject(const Ray& r);
        tuple<HitRecord, Vec3> closestHitLight(const Ray& r);
    };
}

#endif